﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.Threading;
using YEA.Infrastructure.Gateway;
using YEA.Monitor.NotificationManager;
using YEA.Utilities.Logging;
using YEA.Utilities.Service.Controller;

namespace YEA.Monitor.NotificationService 
{
    public class Controller: IServiceController
    {
        private enum Diagnostic { MessageBusUnavailable, FatalForUnknownReasons, Unknown };
        private volatile ServiceStatus status;

        public const string EmailServerConfigKey = "EmailServer";
        public const string NotificationKey = "NotificationReceiverName";
        public const string LogPublisherKey = "LogPublisherName";

        private string emailServer;
        private string notificationReceiver;
        private string logPublisher;

        private Diagnostic diagnosis;
        private IAsyncConsumerProcessor receiver;
        private MessageHandler mh;
        private Logger logger;
        private Stopwatch processTimer;
        private int ExceptionCounter;
        private IGatewayFactory GatewayFactory { get; set; }

        public event EventHandler<EventArgs<ServiceStatus>> StatusChanged;
        
        public Controller()
        {
            processTimer = new Stopwatch();
            status = ServiceStatus.Initiated;
            ExceptionCounter = 0;
            ReadConfig();
        }

        private void ReadConfig()
        {
            emailServer = GetConfigKey(EmailServerConfigKey);
            notificationReceiver = GetConfigKey(NotificationKey);
            logPublisher = GetConfigKey(LogPublisherKey);
        }

        private string GetConfigKey(string key)
        {
            var ret = ConfigurationManager.AppSettings[key];
            if (string.IsNullOrEmpty(ret))
                throw new ConfigurationErrorsException(string.Format("the configuration key {0} is required, please add it to the app config section of your configuration file", key));
            return ret;
        }

        private void SendStatusEvent(ServiceStatus status)
        {
            var e = new EventArgs<ServiceStatus>(status);
            if (StatusChanged != null)
                StatusChanged(this, e);
        }

        public void Start()
        {

            if (receiver == null)
            {
                try
                {
                    
                    //TODO: move to configuraton file
                    logger = new Logger(logPublisher);
                    mh = new MessageHandler(emailServer, logger);
                    StatusChanged += mh.OnChange;
                    //TODO: Move to configuration file.
                    
                    logger.Write("Started", LogType.Information);
                    processTimer.Restart();
                    SendStatusEvent(ServiceStatus.InProgress);
                    using (var gf = new GatewayFactory())
                    {
                        receiver = gf.GetAsyncReceiver(notificationReceiver, mh.ConsumeMessage);
                    }
                }
                catch (Exception ex)
                {
                    if (processTimer.Elapsed > TimeSpan.FromMinutes(10.0))
                        ExceptionCounter = 0;
                    Interpret(ex);
                }
            }
        }




        public void Stop()
        {
            status = ServiceStatus.Stopped;
            receiver = null;
            SendStatusEvent(ServiceStatus.Stopped);
            if( mh != null) 
                mh.Dispose();
        }

        public void Pause()
        {
            if (status == ServiceStatus.InProgress)
            {
                status = ServiceStatus.Paused;
                SendStatusEvent(ServiceStatus.Paused);
            }
        }
        public void Continue()
        {
            if (status == ServiceStatus.Paused)
            {
                status = ServiceStatus.InProgress;
                SendStatusEvent(ServiceStatus.InProgress);
            }
        }
        public void Interpret(Exception e)
        {
            if (status != ServiceStatus.Stopped)
            {
                logger.Write("Service entered Exception interpretation mode", LogType.Information);
                logger.Write(e.Message, LogType.Error);
                if (e is RabbitMQ.Client.Exceptions.OperationInterruptedException
                    || e is RabbitMQ.Client.Exceptions.BrokerUnreachableException
                    || e is RabbitMQ.Client.Exceptions.AlreadyClosedException)
                {
                    SendStatusEvent(ServiceStatus.Disabled);
                    diagnosis = Diagnostic.MessageBusUnavailable;
                    this.Recover(e);
                }
                else
                {
                    Stop();
                    if (ExceptionCounter++ < 100)
                        Start();
                    else
                        logger.Write(e.Message, LogType.Fatal);
                }
            }
        }

        public void Recover(Exception e)
        {
            logger.Write("Service entered Recovery Mode ", LogType.Information);
            SendStatusEvent(ServiceStatus.Recovering);
            while (status != ServiceStatus.Stopped)
            {
                Thread.Sleep(10000);
                this.Start();
            }
        }
    }
}
